package decision_tree;

import interfaces.Model;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Map.Entry;

import dao.PropertyIO;
import decision_tree.dao.DecisionTreeIO;
import decision_tree.dto.Sample;
import decision_tree.dto.TreeNode;
import decision_tree.metric.EntropyManager;
import decision_tree.pos.POSManager;
import decision_tree.question.NgramQuestionGenerator;
import decision_tree.question.Question;
import decision_tree.question.QuestionGenerator;

public class DecisionTreeModel implements Model {
	private static final String CONFIG_FILE = "conf/decision_tree.config";
	
	protected static EntropyManager ep = EntropyManager.getInstance();
	protected static QuestionAnalyzer qa = new QuestionAnalyzer(ep);
	protected static DecisionTreeIO io = new DecisionTreeIO();

	private Properties properties;
	private TreeNode root;
	
	public DecisionTreeModel() {
		properties = PropertyIO.load(CONFIG_FILE);
		if(!POSManager.getInstance().isInitialized()) {
			POSManager.getInstance().initialize(
				properties.getProperty("vocabulary_file"),
				properties.getProperty("equivalence_lass_file"));
		}
	}

	@Override
	public void train(List<String> data) {
		List<Sample> sampleList = getSamples(data);
		root = new TreeNode();
		
		int max_level = Integer.parseInt(properties.getProperty("max_level"));
		
		QuestionGenerator qg = new NgramQuestionGenerator();
		List<Question> questionList = qg.generate(properties);
		OneNode(sampleList, questionList, root, 1, max_level);
	}

	@Override
	public List<Double> test(List<String> data) {
		Sample sample = new Sample();
		sample.setX(data);
				
		TreeNode node = root;
		while(node.model == null) {
			int category = node.q.ask(sample);
			node = node.children.get(category);
		} 
		ProbabilityModel model = node.model;
		return model.getDistribution(sample);
	}

	@Override
	public void write(String filename) {
		io.dump(root, filename);

	}

	@Override
	public void read(String filename) {
		root = io.load(filename);
	}
	
	// protected *********************************************
	
	protected List<Sample> getSamples(List<String> data) {
		data = new ArrayList<String>(data);
		
		List<String> abstractData = new ArrayList<String>(data.size());
		for(String term : data) {
			abstractData.add(POSManager.getInstance().convert(term));
		}
		
		List<Sample> sampleList = new ArrayList<Sample> (data.size());
		for(int i = 0; i < data.size(); i++) {
			Sample sample = new Sample();
			sample.setX(abstractData.subList(0, i));
			sample.setY(data.get(i));
			sampleList.add(sample);
		}
		return sampleList;
	}
	
	protected void OneNode(List<Sample> trainSampleList, List<Question> questionList, TreeNode node, int cur_level, int max_level) {
		
		if(cur_level < max_level) {
			cur_level ++;
			
			Question q = qa.findBestQuestion(trainSampleList, questionList, "out.txt");
			node.q = q;

			List<Question> nextQuestionList = new ArrayList<Question>(questionList);
			nextQuestionList.remove(q);
			
			Map<Integer, List<Sample>> dataMap = ep.splitData(trainSampleList, q);
		
			for(Entry<Integer, List<Sample>> entry : dataMap.entrySet()) {
				TreeNode child = new TreeNode();
				node.children.put(entry.getKey(), child);
				OneNode(entry.getValue(), nextQuestionList, child, cur_level, max_level);
			}
		} else {
			UnigramModel model = new UnigramModel();
			model.train(trainSampleList);
			node.model = model;
		}
	}

	@Override
	public String getName() {
		return "decision_tree";
	}
}
